1 Goals

At the end of this Lab session, we should: - know the types and structures of spatial data and be able to work with them - understand the basics of modern spatial packages in R - be able to specify and download spatial data from the web, using R - plot static and interactive maps using ggplot, tmap and leaflet packages - add symbols and markers for places and regions of our own interest in these maps. - see directions for further work (e.g. maps + networks together)

2 Installation

rgdal is R’s interface to the “Geospatial Abstraction Library (GDAL)” which is used by other open source GIS packages such as QGIS and enables R to handle a broader range of spatial data formats.

2.1 Introduction to Maps in R

We will take small steps in making maps using just two of the several map making packages in R.

The steps we will use are:

  1. Search for an area of interest ( E.g. using prettymapr or similar..)
  2. Learn how to access spatial/map data using osmdata
  3. Plot and dress up our map using osmplot, tmap and also with leaflet.
  4. Create interactive maps with leaflet using a variety of map data providers. Note: tmap can also do interactive maps which we will explore also.

Bas. Onwards and Map-wards!!

2.2 God made me a BengaluR-kaR…I think

Let’s get BLR data into R and see if we can plot an area of interest. Then we can order on Swiggy…never mind.

Where is my home? Specify a “bounding box” first, using a rough longitude latitude info, or using a place name and searching for the long/lat:

# BLR Bounding Box
bbox <- osmplotr::get_bbox(c(77.56,12.93,77.63,12.96))
bbox_l <- osmdata::getbb("Bangalore, India")
bbox_p <- prettymapr::searchbbox("Bangalore")
## Using default API key for pickpoint.io. If batch geocoding, please get your own (free) API key at https://app.pickpoint.io/sign-up
bbox
##     min   max
## x 77.56 77.63
## y 12.93 12.96
bbox_l
##        min      max
## x 77.46010 77.78405
## y 12.83401 13.14366
bbox_p # identical with bbox_l
##        min      max
## x 77.46010 77.78405
## y 12.83401 13.14366
# Get Map data

dat_B <- extract_osm_objects (key = "building", bbox = bbox) 
## Issuing query to Overpass API ...
## Rate limit: 0
## Query complete!
## converting OSM data to sf format
dat_H <- extract_osm_objects (key = 'highway', bbox = bbox)
## Issuing query to Overpass API ...
## Rate limit: 0
## Query complete!
## converting OSM data to sf format
dat_P <- extract_osm_objects (key = 'park', bbox = bbox)
## Issuing query to Overpass API ...
## Rate limit: 0
## Query complete!
## converting OSM data to sf format
dat_G <- extract_osm_objects (key = 'landuse', value = 'grass', bbox = bbox)
## Issuing query to Overpass API ...
## Rate limit: 0
## Query complete!
## converting OSM data to sf format
dat_T <- extract_osm_objects (key = 'natural', value = 'tree', bbox = bbox)
## Issuing query to Overpass API ...
## Rate limit: 0
## Query complete!
## converting OSM data to sf format
#Useful keys include building, waterway, natural, grass, park, amenity, shop, boundary, and highway.

2.3 My first Map in R

blr_map <- osm_basemap(bbox = bbox, bg = "gray20") %>%
  add_osm_objects(., dat_B, col = "gray40") %>% 
  add_osm_objects(., dat_H, col = "gray80") %>% 
  add_osm_objects(., dat_G, col = "darkseagreen1") %>% 
  add_osm_objects(., dat_P, col = "darkseagreen") %>% 
  add_osm_objects(., dat_T, col = "green")

print_osm_map(blr_map)
#print_osm_map (map, filename = "Blr.jpeg", device = "jpeg", width = 10, units = "cm")
#print_osm_map (map, filename = "map.png", width = 10, units = "in", dpi = 300)
blr_map_2 <- osm_basemap(bbox = bbox, bg = "gray20") %>%
  add_osm_objects(., dat_B, col = "gray40") %>% 
  add_osm_objects(., dat_H, col = "gray80") %>% 
  add_osm_objects(., dat_G, col = "darkseagreen1") %>% 
  add_osm_objects(., dat_P, col = "darkseagreen") %>% 
  add_osm_objects(., dat_T, col = "green")

print_osm_map(blr_map)
class(dat_B)
## [1] "sf"         "data.frame"
names(dat_B)
##  [1] "osm_id"                      "name"                       
##  [3] "Friary"                      "addr.city"                  
##  [5] "addr.district"               "addr.full"                  
##  [7] "addr.housename"              "addr.housenumber"           
##  [9] "addr.postcode"               "addr.state"                 
## [11] "addr.street"                 "addr.suburb"                
## [13] "air_conditioning"            "alt_name"                   
## [15] "amenity"                     "area"                       
## [17] "atm"                         "barrier"                    
## [19] "brand"                       "brand.wikidata"             
## [21] "brand.wikipedia"             "building"                   
## [23] "building.colour"             "building.levels"            
## [25] "building.levels.underground" "bus"                        
## [27] "capacity"                    "clothes"                    
## [29] "club"                        "construction"               
## [31] "cuisine"                     "delivery"                   
## [33] "denomination"                "description"                
## [35] "designation"                 "ele"                        
## [37] "email"                       "emergency"                  
## [39] "government"                  "healthcare"                 
## [41] "healthcare.speciality"       "height"                     
## [43] "internet_access"             "landmark"                   
## [45] "landuse"                     "layer"                      
## [47] "leisure"                     "level"                      
## [49] "lit"                         "man_made"                   
## [51] "max_age"                     "min_age"                    
## [53] "name.en"                     "name.kn"                    
## [55] "note"                        "office"                     
## [57] "official_name"               "opening_hours"              
## [59] "operator"                    "operator.type"              
## [61] "organic"                     "outdoor_seating"            
## [63] "payment.cash"                "payment.debit_cards"        
## [65] "phone"                       "public_transport"           
## [67] "religion"                    "screen"                     
## [69] "service"                     "service_times"              
## [71] "shop"                        "smoking"                    
## [73] "source"                      "takeaway"                   
## [75] "tourism"                     "website"                    
## [77] "wheelchair"                  "wikidata"                   
## [79] "wikipedia"                   "geometry"
class(dat_B$geometry)
## [1] "sfc_POLYGON" "sfc"
nrow(dat_B)
## [1] 39699

2.4 Adding my “area” to the map

We can create areas of interest around the map. Say based on metro stations or your restaurants etc.

my_area <- 
  bbox %>% 
  zoombbox(factor = 16, offset = c(0,0)) 
my_area
##        min      max
## x 77.59281 77.59719
## y 12.94406 12.94594
bbox
##     min   max
## x 77.56 77.63
## y 12.93 12.96
my_area_in_blr <- 
  sp::SpatialPoints(coords = cbind(
  c(my_area["x", "min"], my_area["x", "max"],
    my_area["x", "max"], my_area["x", "min"]),
  c(my_area["y", "min"], my_area["y", "min"],
    my_area["y", "max"], my_area["y", "max"])
        )
  )
my_area_in_blr
## class       : SpatialPoints 
## features    : 4 
## extent      : 77.59281, 77.59719, 12.94406, 12.94594  (xmin, xmax, ymin, ymax)
## crs         : NA
blr_map %>% add_osm_groups(
  .,
  dat_B,
  groups = my_area_in_blr,
  cols = "yellow",
  bg = "gray40",
  colmat = FALSE
) %>%
  add_osm_objects(., dat_B, col = "red", size = 0.2) %>% 
  print_osm_map(.)
2.4.0.0.1 END OF CLASS 1

2.5 Using rnaturalearth and tmap

data("World")
data("metro")

head(metro, n = 3)
## Simple feature collection with 3 features and 12 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 3.04197 ymin: -8.83682 xmax: 69.17246 ymax: 36.7525
## Geodetic CRS:  WGS 84
##       name             name_long iso_a3 pop1950 pop1960 pop1970 pop1980 pop1990
## 2    Kabul                 Kabul    AFG  170784  285352  471891  977824 1549320
## 8  Algiers El Djazair  (Algiers)    DZA  516450  871636 1281127 1621442 1797068
## 13  Luanda                Luanda    AGO  138413  219427  459225  771349 1390240
##    pop2000 pop2010 pop2020  pop2030                  geometry
## 2  2401109 3722320 5721697  8279607 POINT (69.17246 34.52889)
## 8  2140577 2432023 2835218  3404575   POINT (3.04197 36.7525)
## 13 2591388 4508434 6836849 10428756 POINT (13.23432 -8.83682)
tmap_mode("plot")
## tmap mode set to plotting
tm_shape(World) +
    tm_polygons("HPI") +
  
tm_shape(metro) + 
  tm_bubbles(size = "pop2020", col = "red")

tmap_mode("view")
## tmap mode set to interactive viewing
tm_shape(World) +
    tm_polygons("HPI") +
tm_shape(metro) + 
  tm_bubbles(size = "pop2020", col = "red") +
tm_tiles("Stamen.TonerLabels")
## Legend for symbol sizes not available in view mode.
india <- 
  ne_states(country =  "india", 
            geounit = "india", 
            returnclass = "sf")

india_neighbours <- 
  ne_states(country= (c("india", "sri lanka", "pakistan", "afghanistan", "nepal","bangladesh")))

names(india)
##  [1] "featurecla" "scalerank"  "adm1_code"  "diss_me"    "iso_3166_2"
##  [6] "wikipedia"  "iso_a2"     "adm0_sr"    "name"       "name_alt"  
## [11] "name_local" "type"       "type_en"    "code_local" "code_hasc" 
## [16] "note"       "hasc_maybe" "region"     "region_cod" "provnum_ne"
## [21] "gadm_level" "check_me"   "datarank"   "abbrev"     "postal"    
## [26] "area_sqkm"  "sameascity" "labelrank"  "name_len"   "mapcolor9" 
## [31] "mapcolor13" "fips"       "fips_alt"   "woe_id"     "woe_label" 
## [36] "woe_name"   "latitude"   "longitude"  "sov_a3"     "adm0_a3"   
## [41] "adm0_label" "admin"      "geonunit"   "gu_a3"      "gn_id"     
## [46] "gn_name"    "gns_id"     "gns_name"   "gn_level"   "gn_region" 
## [51] "gn_a1_code" "region_sub" "sub_code"   "gns_level"  "gns_lang"  
## [56] "gns_adm1"   "gns_region" "min_label"  "max_label"  "min_zoom"  
## [61] "wikidataid" "name_ar"    "name_bn"    "name_de"    "name_en"   
## [66] "name_es"    "name_fr"    "name_el"    "name_hi"    "name_hu"   
## [71] "name_id"    "name_it"    "name_ja"    "name_ko"    "name_nl"   
## [76] "name_pl"    "name_pt"    "name_ru"    "name_sv"    "name_tr"   
## [81] "name_vi"    "name_zh"    "ne_id"      "geometry"
names(india_neighbours)
##  [1] "featurecla" "scalerank"  "adm1_code"  "diss_me"    "iso_3166_2"
##  [6] "wikipedia"  "iso_a2"     "adm0_sr"    "name"       "name_alt"  
## [11] "name_local" "type"       "type_en"    "code_local" "code_hasc" 
## [16] "note"       "hasc_maybe" "region"     "region_cod" "provnum_ne"
## [21] "gadm_level" "check_me"   "datarank"   "abbrev"     "postal"    
## [26] "area_sqkm"  "sameascity" "labelrank"  "name_len"   "mapcolor9" 
## [31] "mapcolor13" "fips"       "fips_alt"   "woe_id"     "woe_label" 
## [36] "woe_name"   "latitude"   "longitude"  "sov_a3"     "adm0_a3"   
## [41] "adm0_label" "admin"      "geonunit"   "gu_a3"      "gn_id"     
## [46] "gn_name"    "gns_id"     "gns_name"   "gn_level"   "gn_region" 
## [51] "gn_a1_code" "region_sub" "sub_code"   "gns_level"  "gns_lang"  
## [56] "gns_adm1"   "gns_region" "min_label"  "max_label"  "min_zoom"  
## [61] "wikidataid" "name_ar"    "name_bn"    "name_de"    "name_en"   
## [66] "name_es"    "name_fr"    "name_el"    "name_hi"    "name_hu"   
## [71] "name_id"    "name_it"    "name_ja"    "name_ko"    "name_nl"   
## [76] "name_pl"    "name_pt"    "name_ru"    "name_sv"    "name_tr"   
## [81] "name_vi"    "name_zh"    "ne_id"

2.6 Subsetting Spatial data by attributes

#ind <- metro$iso_a3 == "IND"
#metro_ind1 <- metro[ind,]
metro_ind2 <- subset(metro, iso_a3 == "IND")
metro_neighbours <- metro %>% dplyr::filter(iso_a3 %in% c("IND","PAK", "LKA", "BGD","NPL"))

2.7 Map 1

tm_shape(World %>% dplyr::filter(iso_a3 %in% c("IND", "AFG", "PAK", "NPL", "BGD", "LKA"))) + 
  tm_borders() + 
tm_shape(india) + 
  tm_polygons("name") + 
  tm_shape(metro_ind2)+
  tm_dots(size = "pop2020") + 
  tm_layout(legend.outside = TRUE,legend.outside.position = "right") +
  tm_credits("Geographical Boundaries are not accurate",size = 0.5,position = "right") + 
  tm_compass(position = c("right", "top")) + 
  tm_scale_bar(position = "left") + 
  tmap_style("watercolor") #cobalt #gray #white #col_blind #beaver #classic #watercolor #albatross #bw
## tmap style set to "watercolor"
## other available styles are: "white", "gray", "natural", "cobalt", "col_blind", "albatross", "beaver", "bw", "classic"
## Credits not supported in view mode.
## Compass not supported in view mode.
## Warning: Number of levels of the variable "name" is 35, which is
## larger than max.categories (which is 30), so levels are combined. Set
## tmap_options(max.categories = 35) in the layer function to show all levels.
## Legend for symbol sizes not available in view mode.

2.8 Map 2

tm_shape(india_neighbours) + 
  tm_polygons("name") + 
  tm_shape(metro_neighbours) +
  tm_dots(size = "pop2020") + 
  tm_layout(legend.outside = TRUE,legend.outside.position = "right") +
  tmap_options(max.categories = 10) + 
  tm_credits("Geographical Boundaries are not accurate",size = 0.5,position = "center")
## Credits not supported in view mode.
## Warning: Number of levels of the variable "name" is 112, which is
## larger than max.categories (which is 10), so levels are combined. Set
## tmap_options(max.categories = 112) in the layer function to show all levels.
## Legend for symbol sizes not available in view mode.
tmap_mode("plot")
## tmap mode set to plotting
tm_basemap("Stamen.Watercolor") +
  tm_shape(metro, bbox = "India") + 
  tm_dots(col = "red", 
          # user-chosen group name for layers
          group = "Metropolitan Areas") +
  tm_shape(World) + 
  tm_borders() + 
  tm_tiles(server = "Stamen.TonerLabels",group = "Labels") + # ADDS LABELS!!!
  tm_graticules()

2.9 Scope and Packages for Exploration!!

2.9.1 sfnetworks

2.9.2 mapsf

2.9.3 ggspatial

2.10 Resources

  1. Emine Fidan, Guide to Creating Interactive Maps in R
LS0tDQp0aXRsZTogIkxhYi0wNjogVGhlIEdyYW1tYXIgb2YgTWFwcyINCnN1YnRpdGxlOiAiV2hpY2ggaXMgeW91ciBuYXRpdmU/Ig0KYXV0aG9yOiAiQXJ2aW5kIFZlbmthdGFkcmkiDQpkYXRlOiAyMi9BcHJpbC8yMDIxDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdGhlbWU6IGZsYXRseQ0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIHRvY19kZXB0aDogMg0KICAgIG51bWJlcl9zZWN0aW9uczogVFJVRQ0KICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUNCmFic3RyYWN0OiBQYXJ0IG9mIHRoZSBgUiBmb3IgQXJ0aXN0cyBhbmQgRGVzaWduZXJzYCBjb3Vyc2UgYXQgdGhlIFNjaG9vbCBvZiBGb3VuZGF0aW9uIFN0dWRpZXMsIFNyaXNodGkgTWFuaXBhbCBJbnN0aXR1dGUgb2YgQXJ0LCBEZXNpZ24sIGFuZCBUZWNobm9sb2d5LCBCYW5nYWxvcmUuDQotLS0NCg0KIyBHb2Fscw0KDQpBdCB0aGUgZW5kIG9mIHRoaXMgTGFiIHNlc3Npb24sIHdlIHNob3VsZDoNCi0ga25vdyB0aGUgdHlwZXMgYW5kIHN0cnVjdHVyZXMgb2YgYHNwYXRpYWwgZGF0YWAgYW5kIGJlIGFibGUgdG8gd29yayB3aXRoIHRoZW0NCi0gdW5kZXJzdGFuZCB0aGUgYmFzaWNzIG9mIG1vZGVybiBzcGF0aWFsIHBhY2thZ2VzIGluIFINCi0gYmUgYWJsZSB0byBzcGVjaWZ5IGFuZCBkb3dubG9hZCBzcGF0aWFsIGRhdGEgZnJvbSB0aGUgd2ViLCB1c2luZyBSDQotIHBsb3Qgc3RhdGljIGFuZCBpbnRlcmFjdGl2ZSBtYXBzIHVzaW5nIGBnZ3Bsb3RgLCBgdG1hcGAgYW5kIGBsZWFmbGV0YCBwYWNrYWdlcw0KLSBhZGQgc3ltYm9scyBhbmQgbWFya2VycyBmb3IgcGxhY2VzIGFuZCByZWdpb25zIG9mIG91ciBvd24gaW50ZXJlc3QgaW4gdGhlc2UgbWFwcy4NCi0gc2VlIGRpcmVjdGlvbnMgZm9yIGZ1cnRoZXIgd29yayAoZS5nLiBtYXBzICsgbmV0d29ya3MgdG9nZXRoZXIpDQoNCiMgSW5zdGFsbGF0aW9uDQoqKnJnZGFsKiogaXMgUuKAmXMgaW50ZXJmYWNlIHRvIHRoZSAiR2Vvc3BhdGlhbCBBYnN0cmFjdGlvbiBMaWJyYXJ5IChHREFMKSIgd2hpY2ggaXMgdXNlZCBieSBvdGhlciBvcGVuIHNvdXJjZSBHSVMgcGFja2FnZXMgc3VjaCBhcyBRR0lTIGFuZCBlbmFibGVzIFIgdG8gaGFuZGxlIGEgYnJvYWRlciByYW5nZSBvZiBzcGF0aWFsIGRhdGEgZm9ybWF0cy4NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQojIEdldHRpbmcgTWFwIERhdGEgaW50byBSDQpsaWJyYXJ5KHByZXR0eW1hcHIpICMgdG8gc2VhcmNoIGZvciBtYXAgZGF0YSBiYXNlZCBvbiBsb2NhdGlvbg0KDQpsaWJyYXJ5KG9zbWRhdGEpICMgSW1wb3J0IE9wZW4gU3RyZWV0IERhdGENCmxpYnJhcnkocm5hdHVyYWxlYXJ0aCkNCmxpYnJhcnkocm5hdHVyYWxlYXJ0aGRhdGEpICMgSW1wb3J0IE5hdHVyYWwgRWFydGggRGF0YQ0KbGlicmFyeShybmF0dXJhbGVhcnRoaGlyZXMpICMgSGkgUmVzb2x1dGlvbi4uaXMgeW91ciBXaUZpIGFueSBnb29kPw0KDQojIFBsb3R0aW5nIE1hcHMNCmxpYnJhcnkob3NtcGxvdHIpICMgIkJlc3Bva2UiIE1hcHMgdXNpbmcgT1NNIGRhdGENCmxpYnJhcnkodG1hcCkgIyBUaGVtYXRpYyBNYXBzLCBzdGF0aWMgYW5kIGludGVyYWN0aXZlDQpsaWJyYXJ5KGxlYWZsZXQpICMgSW50ZXJhY3RpdmUgTWFwcw0KbGlicmFyeShsZWFmbGV0LnByb3ZpZGVycykNCg0KYGBgDQoNCiMjIEludHJvZHVjdGlvbiB0byBNYXBzIGluIFINCg0KV2Ugd2lsbCB0YWtlIHNtYWxsIHN0ZXBzIGluIG1ha2luZyBtYXBzIHVzaW5nIGp1c3QgdHdvIG9mIHRoZSBzZXZlcmFsIG1hcCBtYWtpbmcgcGFja2FnZXMgaW4gUi4NCg0KVGhlIHN0ZXBzIHdlIHdpbGwgdXNlIGFyZToNCg0KMS4gU2VhcmNoIGZvciBhbiBhcmVhIG9mIGludGVyZXN0ICggRS5nLiB1c2luZyBgcHJldHR5bWFwcmAgb3Igc2ltaWxhci4uKQ0KMi4gTGVhcm4gaG93IHRvIGFjY2VzcyBzcGF0aWFsL21hcCBkYXRhIHVzaW5nIGBvc21kYXRhYA0KMy4gUGxvdCBhbmQgZHJlc3MgdXAgb3VyIG1hcCB1c2luZyBgb3NtcGxvdGAsIGB0bWFwYCBhbmQgYWxzbyB3aXRoIGBsZWFmbGV0YC4NCjQuIENyZWF0ZSBpbnRlcmFjdGl2ZSBtYXBzIHdpdGggYGxlYWZsZXRgIHVzaW5nIGEgdmFyaWV0eSBvZiBtYXAgZGF0YSBwcm92aWRlcnMuIE5vdGU6IGB0bWFwYCBjYW4gYWxzbyBkbyBpbnRlcmFjdGl2ZSBtYXBzIHdoaWNoIHdlIHdpbGwgZXhwbG9yZSBhbHNvLiANCg0KQmFzLiBPbndhcmRzIGFuZCBNYXAtd2FyZHMhIQ0KDQoNCiMjIEdvZCBtYWRlIG1lIGEgQmVuZ2FsdVIta2FSLi4uSSB0aGluaw0KDQpMZXQncyBnZXQgQkxSIGRhdGEgaW50byBSIGFuZCBzZWUgaWYgd2UgY2FuIHBsb3QgYW4gYXJlYSBvZiBpbnRlcmVzdC4gVGhlbiB3ZSBjYW4gb3JkZXIgb24gU3dpZ2d5Li4ubmV2ZXIgbWluZC4gDQoNCldoZXJlIGlzIG15IGhvbWU/IFNwZWNpZnkgYSAiYm91bmRpbmcgYm94IiBmaXJzdCwgdXNpbmcgYSByb3VnaCBsb25naXR1ZGUgbGF0aXR1ZGUgaW5mbywgb3IgdXNpbmcgYSBwbGFjZSBuYW1lIGFuZCBzZWFyY2hpbmcgZm9yIHRoZSBsb25nL2xhdDoNCg0KYGBge3IgSV9hbV9nb2luZ19ob21lfQ0KIyBCTFIgQm91bmRpbmcgQm94DQpiYm94IDwtIG9zbXBsb3RyOjpnZXRfYmJveChjKDc3LjU2LDEyLjkzLDc3LjYzLDEyLjk2KSkNCmJib3hfbCA8LSBvc21kYXRhOjpnZXRiYigiQmFuZ2Fsb3JlLCBJbmRpYSIpDQpiYm94X3AgPC0gcHJldHR5bWFwcjo6c2VhcmNoYmJveCgiQmFuZ2Fsb3JlIikNCmJib3gNCmJib3hfbA0KYmJveF9wICMgaWRlbnRpY2FsIHdpdGggYmJveF9sDQpgYGANCg0KYGBge3IgZ2V0X29zbV9tYXBfZGF0YSwgY2FjaGUgPSBUUlVFfQ0KIyBHZXQgTWFwIGRhdGENCg0KZGF0X0IgPC0gZXh0cmFjdF9vc21fb2JqZWN0cyAoa2V5ID0gImJ1aWxkaW5nIiwgYmJveCA9IGJib3gpIA0KZGF0X0ggPC0gZXh0cmFjdF9vc21fb2JqZWN0cyAoa2V5ID0gJ2hpZ2h3YXknLCBiYm94ID0gYmJveCkNCmRhdF9QIDwtIGV4dHJhY3Rfb3NtX29iamVjdHMgKGtleSA9ICdwYXJrJywgYmJveCA9IGJib3gpDQpkYXRfRyA8LSBleHRyYWN0X29zbV9vYmplY3RzIChrZXkgPSAnbGFuZHVzZScsIHZhbHVlID0gJ2dyYXNzJywgYmJveCA9IGJib3gpDQpkYXRfVCA8LSBleHRyYWN0X29zbV9vYmplY3RzIChrZXkgPSAnbmF0dXJhbCcsIHZhbHVlID0gJ3RyZWUnLCBiYm94ID0gYmJveCkNCg0KI1VzZWZ1bCBrZXlzIGluY2x1ZGUgYnVpbGRpbmcsIHdhdGVyd2F5LCBuYXR1cmFsLCBncmFzcywgcGFyaywgYW1lbml0eSwgc2hvcCwgYm91bmRhcnksIGFuZCBoaWdod2F5Lg0KDQoNCmBgYA0KDQojIyBNeSBmaXJzdCBNYXAgaW4gUg0KDQpgYGB7ciBwbG90IG9zbSBkYXRhfQ0KDQpibHJfbWFwIDwtIG9zbV9iYXNlbWFwKGJib3ggPSBiYm94LCBiZyA9ICJncmF5MjAiKSAlPiUNCiAgYWRkX29zbV9vYmplY3RzKC4sIGRhdF9CLCBjb2wgPSAiZ3JheTQwIikgJT4lIA0KICBhZGRfb3NtX29iamVjdHMoLiwgZGF0X0gsIGNvbCA9ICJncmF5ODAiKSAlPiUgDQogIGFkZF9vc21fb2JqZWN0cyguLCBkYXRfRywgY29sID0gImRhcmtzZWFncmVlbjEiKSAlPiUgDQogIGFkZF9vc21fb2JqZWN0cyguLCBkYXRfUCwgY29sID0gImRhcmtzZWFncmVlbiIpICU+JSANCiAgYWRkX29zbV9vYmplY3RzKC4sIGRhdF9ULCBjb2wgPSAiZ3JlZW4iKQ0KDQpwcmludF9vc21fbWFwKGJscl9tYXApDQojcHJpbnRfb3NtX21hcCAobWFwLCBmaWxlbmFtZSA9ICJCbHIuanBlZyIsIGRldmljZSA9ICJqcGVnIiwgd2lkdGggPSAxMCwgdW5pdHMgPSAiY20iKQ0KI3ByaW50X29zbV9tYXAgKG1hcCwgZmlsZW5hbWUgPSAibWFwLnBuZyIsIHdpZHRoID0gMTAsIHVuaXRzID0gImluIiwgZHBpID0gMzAwKQ0KYGBgDQoNCg0KYGBge3J9DQpibHJfbWFwXzIgPC0gb3NtX2Jhc2VtYXAoYmJveCA9IGJib3gsIGJnID0gImdyYXkyMCIpICU+JQ0KICBhZGRfb3NtX29iamVjdHMoLiwgZGF0X0IsIGNvbCA9ICJncmF5NDAiKSAlPiUgDQogIGFkZF9vc21fb2JqZWN0cyguLCBkYXRfSCwgY29sID0gImdyYXk4MCIpICU+JSANCiAgYWRkX29zbV9vYmplY3RzKC4sIGRhdF9HLCBjb2wgPSAiZGFya3NlYWdyZWVuMSIpICU+JSANCiAgYWRkX29zbV9vYmplY3RzKC4sIGRhdF9QLCBjb2wgPSAiZGFya3NlYWdyZWVuIikgJT4lIA0KICBhZGRfb3NtX29iamVjdHMoLiwgZGF0X1QsIGNvbCA9ICJncmVlbiIpDQoNCnByaW50X29zbV9tYXAoYmxyX21hcCkNCmBgYA0KDQpgYGB7ciBsb29rIGF0IE9TTSBkYXRhLCBtZXNzYWdlPUZBTFNFfQ0KY2xhc3MoZGF0X0IpDQpuYW1lcyhkYXRfQikNCmNsYXNzKGRhdF9CJGdlb21ldHJ5KQ0KbnJvdyhkYXRfQikNCmBgYA0KDQojIyBBZGRpbmcgbXkgImFyZWEiIHRvIHRoZSBtYXANCg0KV2UgY2FuIGNyZWF0ZSBhcmVhcyBvZiBpbnRlcmVzdCBhcm91bmQgdGhlIG1hcC4gU2F5IGJhc2VkIG9uIG1ldHJvIHN0YXRpb25zIG9yIHlvdXIgcmVzdGF1cmFudHMgZXRjLiANCg0KYGBge3Igc2hhcGVzX29uX21hcHN9DQoNCm15X2FyZWEgPC0gDQogIGJib3ggJT4lIA0KICB6b29tYmJveChmYWN0b3IgPSAxNiwgb2Zmc2V0ID0gYygwLDApKSANCm15X2FyZWENCmJib3gNCg0KDQpteV9hcmVhX2luX2JsciA8LSANCiAgc3A6OlNwYXRpYWxQb2ludHMoY29vcmRzID0gY2JpbmQoDQogIGMobXlfYXJlYVsieCIsICJtaW4iXSwgbXlfYXJlYVsieCIsICJtYXgiXSwNCiAgICBteV9hcmVhWyJ4IiwgIm1heCJdLCBteV9hcmVhWyJ4IiwgIm1pbiJdKSwNCiAgYyhteV9hcmVhWyJ5IiwgIm1pbiJdLCBteV9hcmVhWyJ5IiwgIm1pbiJdLA0KICAgIG15X2FyZWFbInkiLCAibWF4Il0sIG15X2FyZWFbInkiLCAibWF4Il0pDQogICAgICAgICkNCiAgKQ0KbXlfYXJlYV9pbl9ibHINCmBgYA0KDQoNCmBgYHtyIE15X0Jscl9maW5hbGx5fQ0KYmxyX21hcCAlPiUgYWRkX29zbV9ncm91cHMoDQogIC4sDQogIGRhdF9CLA0KICBncm91cHMgPSBteV9hcmVhX2luX2JsciwNCiAgY29scyA9ICJ5ZWxsb3ciLA0KICBiZyA9ICJncmF5NDAiLA0KICBjb2xtYXQgPSBGQUxTRQ0KKSAlPiUNCiAgYWRkX29zbV9vYmplY3RzKC4sIGRhdF9CLCBjb2wgPSAicmVkIiwgc2l6ZSA9IDAuMikgJT4lIA0KICBwcmludF9vc21fbWFwKC4pDQpgYGANCg0KIyMjIyMgRU5EIE9GIENMQVNTIDENCg0KDQoNCiMjIFVzaW5nIGBybmF0dXJhbGVhcnRoYCBhbmQgYHRtYXBgDQoNCmBgYHtyIFdvcmxkX0RhdGF9DQpkYXRhKCJXb3JsZCIpDQpkYXRhKCJtZXRybyIpDQoNCmhlYWQobWV0cm8sIG4gPSAzKQ0KYGBgDQoNCg0KYGBge3IgTXkgU3RhdGljIFdvcmxkfQ0KdG1hcF9tb2RlKCJwbG90IikNCnRtX3NoYXBlKFdvcmxkKSArDQogICAgdG1fcG9seWdvbnMoIkhQSSIpICsNCiAgDQp0bV9zaGFwZShtZXRybykgKyANCiAgdG1fYnViYmxlcyhzaXplID0gInBvcDIwMjAiLCBjb2wgPSAicmVkIikNCmBgYA0KDQoNCg0KYGBge3IgTXkgSW50ZXJhY3RpdmUgV2F0ZXIgQ29sb3VyIFdvcmxkfQ0KdG1hcF9tb2RlKCJ2aWV3IikNCg0KdG1fc2hhcGUoV29ybGQpICsNCiAgICB0bV9wb2x5Z29ucygiSFBJIikgKw0KdG1fc2hhcGUobWV0cm8pICsgDQogIHRtX2J1YmJsZXMoc2l6ZSA9ICJwb3AyMDIwIiwgY29sID0gInJlZCIpICsNCnRtX3RpbGVzKCJTdGFtZW4uVG9uZXJMYWJlbHMiKQ0KYGBgDQoNCg0KYGBge3Igc3BhdGlhbF9kYXRhfQ0KaW5kaWEgPC0gDQogIG5lX3N0YXRlcyhjb3VudHJ5ID0gICJpbmRpYSIsIA0KICAgICAgICAgICAgZ2VvdW5pdCA9ICJpbmRpYSIsIA0KICAgICAgICAgICAgcmV0dXJuY2xhc3MgPSAic2YiKQ0KDQppbmRpYV9uZWlnaGJvdXJzIDwtIA0KICBuZV9zdGF0ZXMoY291bnRyeT0gKGMoImluZGlhIiwgInNyaSBsYW5rYSIsICJwYWtpc3RhbiIsICJhZmdoYW5pc3RhbiIsICJuZXBhbCIsImJhbmdsYWRlc2giKSkpDQoNCm5hbWVzKGluZGlhKQ0KbmFtZXMoaW5kaWFfbmVpZ2hib3VycykNCmBgYA0KDQoNCiMjIFN1YnNldHRpbmcgU3BhdGlhbCBkYXRhIGJ5IGF0dHJpYnV0ZXMNCg0KYGBge3IgU3BhdGlhbF9kYXRhX3N1YnNldHRpbmdfYnlfYXR0cmlidXRlc30NCiNpbmQgPC0gbWV0cm8kaXNvX2EzID09ICJJTkQiDQojbWV0cm9faW5kMSA8LSBtZXRyb1tpbmQsXQ0KbWV0cm9faW5kMiA8LSBzdWJzZXQobWV0cm8sIGlzb19hMyA9PSAiSU5EIikNCm1ldHJvX25laWdoYm91cnMgPC0gbWV0cm8gJT4lIGRwbHlyOjpmaWx0ZXIoaXNvX2EzICVpbiUgYygiSU5EIiwiUEFLIiwgIkxLQSIsICJCR0QiLCJOUEwiKSkNCmBgYA0KDQojIyBNYXAgMQ0KYGBge3IgTWFwXzF9DQp0bV9zaGFwZShXb3JsZCAlPiUgZHBseXI6OmZpbHRlcihpc29fYTMgJWluJSBjKCJJTkQiLCAiQUZHIiwgIlBBSyIsICJOUEwiLCAiQkdEIiwgIkxLQSIpKSkgKyANCiAgdG1fYm9yZGVycygpICsgDQp0bV9zaGFwZShpbmRpYSkgKyANCiAgdG1fcG9seWdvbnMoIm5hbWUiKSArIA0KICB0bV9zaGFwZShtZXRyb19pbmQyKSsNCiAgdG1fZG90cyhzaXplID0gInBvcDIwMjAiKSArIA0KICB0bV9sYXlvdXQobGVnZW5kLm91dHNpZGUgPSBUUlVFLGxlZ2VuZC5vdXRzaWRlLnBvc2l0aW9uID0gInJpZ2h0IikgKw0KICB0bV9jcmVkaXRzKCJHZW9ncmFwaGljYWwgQm91bmRhcmllcyBhcmUgbm90IGFjY3VyYXRlIixzaXplID0gMC41LHBvc2l0aW9uID0gInJpZ2h0IikgKyANCiAgdG1fY29tcGFzcyhwb3NpdGlvbiA9IGMoInJpZ2h0IiwgInRvcCIpKSArIA0KICB0bV9zY2FsZV9iYXIocG9zaXRpb24gPSAibGVmdCIpICsgDQogIHRtYXBfc3R5bGUoIndhdGVyY29sb3IiKSAjY29iYWx0ICNncmF5ICN3aGl0ZSAjY29sX2JsaW5kICNiZWF2ZXIgI2NsYXNzaWMgI3dhdGVyY29sb3IgI2FsYmF0cm9zcyAjYncNCmBgYA0KDQoNCg0KIyMgTWFwIDINCmBgYHtyIE1hcF8yfQ0KdG1fc2hhcGUoaW5kaWFfbmVpZ2hib3VycykgKyANCiAgdG1fcG9seWdvbnMoIm5hbWUiKSArIA0KICB0bV9zaGFwZShtZXRyb19uZWlnaGJvdXJzKSArDQogIHRtX2RvdHMoc2l6ZSA9ICJwb3AyMDIwIikgKyANCiAgdG1fbGF5b3V0KGxlZ2VuZC5vdXRzaWRlID0gVFJVRSxsZWdlbmQub3V0c2lkZS5wb3NpdGlvbiA9ICJyaWdodCIpICsNCiAgdG1hcF9vcHRpb25zKG1heC5jYXRlZ29yaWVzID0gMTApICsgDQogIHRtX2NyZWRpdHMoIkdlb2dyYXBoaWNhbCBCb3VuZGFyaWVzIGFyZSBub3QgYWNjdXJhdGUiLHNpemUgPSAwLjUscG9zaXRpb24gPSAiY2VudGVyIikNCmBgYA0KDQpgYGB7cn0NCnRtYXBfbW9kZSgicGxvdCIpDQp0bV9iYXNlbWFwKCJTdGFtZW4uV2F0ZXJjb2xvciIpICsNCiAgdG1fc2hhcGUobWV0cm8sIGJib3ggPSAiSW5kaWEiKSArIA0KICB0bV9kb3RzKGNvbCA9ICJyZWQiLCANCiAgICAgICAgICAjIHVzZXItY2hvc2VuIGdyb3VwIG5hbWUgZm9yIGxheWVycw0KICAgICAgICAgIGdyb3VwID0gIk1ldHJvcG9saXRhbiBBcmVhcyIpICsNCiAgdG1fc2hhcGUoV29ybGQpICsgDQogIHRtX2JvcmRlcnMoKSArIA0KICB0bV90aWxlcyhzZXJ2ZXIgPSAiU3RhbWVuLlRvbmVyTGFiZWxzIixncm91cCA9ICJMYWJlbHMiKSArICMgQUREUyBMQUJFTFMhISENCiAgdG1fZ3JhdGljdWxlcygpDQoNCmBgYA0KDQoNCiMjIFNjb3BlIGFuZCBQYWNrYWdlcyBmb3IgRXhwbG9yYXRpb24hIQ0KDQojIyMgc2ZuZXR3b3Jrcw0KIyMjIG1hcHNmDQojIyMgZ2dzcGF0aWFsDQoNCg0KIyMgUmVzb3VyY2VzDQoNCjEuIEVtaW5lIEZpZGFuLCBbR3VpZGUgdG8gQ3JlYXRpbmcgSW50ZXJhY3RpdmUgTWFwcyBpbiBSXShodHRwczovL2Jvb2tkb3duLm9yZy9lbmVtaW5lZi9EUlJfQm9va2Rvd24vKQ0KDQoNCg==